<?php
/**
 * Celebrio FileSystem module
 *
 * @copyright  Copyright (c) 2011 Celebrio Software
 * @package    Celebrio
 * @subpackage FileSystem
 */
namespace Celebrio\FileSystem;

use Nette\Object;

use InvalidArgumentException;
use dibi;

use Celebrio\FileSystem\IFile;
use Celebrio\FileSystem\VirtualFile;
use Celebrio\FileSystem\Drivers\Local;
use Celebrio\FileSystem\FileInfo;
use Celebrio\FileSystem\GenericFile;

use Celebrio\Diagnostics\Timer;
use Celebrio\Diagnostics\TimerLevel;


/**
 * DataFile represents the structure given/recieved on the interface
 * of the VFS Manager. It contains the binary data from file and
 * some other attributes about the file such as size or mimetype.
 * The url attribute represents the virtual URL of the file.
 *
 * @author pavel
 */
class DataFile extends GenericFile implements IFile {
    
    const DIR = "dir";
    
    /**
     * This file storage name will be used when user does not specify any.
     * It can be set to null when you don't want to use default fstorage, but then
     * every request on the file which does not exist yet will HAVE TO 
     * specify the required fstorage type.
     */
    private static $DEFAULT_FSTORAGE_NAME;

    /* INHERITS ATTRIBUTES FROM GENERIC_FILE */

    /** @var binary raw data from file */
    private $data = null;
    
    /**
     * @var FileInfo attribute containing all boolean and some id-based attributes.
     */
    private $fileInfo = null;
    
    private $size = null;
    
    /**
     * @var String Name (type) of the storage the file is (or will be) saved in.
     */
    private $fstorageName = null;
    
    private $mimetypeText;
    
    public function __construct($name, $url, $mimetypeText = NULL, $fstorageName = null) {
        self::setDefaultFstorageName();
        
        parent::__construct($name, $url);
        if($mimetypeText === NULL) {
        	$fileExtension = strtolower(substr(strrchr($url,'.'),1));
        	switch($fileExtension){
        		case "jpg":
        		case "jpeg":
        			$mimetypeText = image_type_to_mime_type(IMAGETYPE_JPEG);
        			break;
        		case "png":
        			$mimetypeText = image_type_to_mime_type(IMAGETYPE_PNG);
        			break;
        		case "gif":
        			$mimetypeText = image_type_to_mime_type(IMAGETYPE_GIF);
        			break;
        		case "vmw":
        			$mimetypeText = "video/x-ms-wmv";
        			break;
        		case "mp4":
        			$mimetypeText = "video/mp4";
        			break;
        	}
        }
        $this->setMimetypeText($mimetypeText);
        $this->setFstorageName($fstorageName);
    }
     
    /**
     * Sets DEFAULT_FSTORAGE_NAME "constant".
     * 
     * @todo should be loaded dynamicaly according to the system
     * preferences (from config).
     */
    private static function setDefaultFstorageName() {
        self::$DEFAULT_FSTORAGE_NAME = Local::NAME;
    }
    
    public static function DEFAULT_FSTORAGE_NAME() {
        return self::$DEFAULT_FSTORAGE_NAME;
    }

    /**
     * Checks whether the URL is valid.
     * The URL must start with '/' prefix.
     * Other rules are handled by (parent) GenericFile::setUrl() method.
     *
     * @throws Exception when the name or url is invalid
     * @todo change the exception type and catch it
     */
    public function  setUrl($url) {
        if ($this->isRoot($url, $this->name, true)) {
            $this->url = null;
        } elseif ((substr($url, 0, 1)) === "/") {
            parent::setUrl($url);
        } else {
            throw new InvalidArgumentException("URL must start with slash '/'.");
        }
    }

    public function getData() {
        return $this->data;
    }

    public function setData($data) {
        $this->data = $data;
    }

    public function setFileInfo(FileInfo $fileInfo) {
        $this->fileInfo = $fileInfo;
    }

    public function getFileInfo() {
        return $this->fileInfo;
    }

    public function getSize() {
        return $this->size;
    }

    public function setSize($size) {
        $this->size = $size;
    }
    
    public function getFstorageName() {
        return $this->fstorageName;
    }

    public function setFstorageName($fstorageName) {
        $this->fstorageName = $fstorageName;
    }
    
    public function getMimetypeText() {
        return $this->mimetypeText;
    }
    
    /**
     * @param String $mimetype
     */
    public function setMimetypeText($mimetypeText) {
        $this->mimetypeText = $mimetypeText;
    }
    
/* ------------------------ FILE INFO SETTERS ---------------------------- */
    
    private function setEncrypted($encrypted) {
        if ($this->getFileInfo() === null) {
            $this->setFileInfo(new FileInfo());
        }
        $this->getFileInfo()->setEncrypted($encrypted);
    }
    
    private function setProtected($protected) {
        if ($this->getFileInfo() === null) {
            $this->setFileInfo(new FileInfo());
        }
        $this->getFileInfo()->setProtected($protected);
    }
    
    private function setHidden($hidden) {
        if ($this->getFileInfo() === null) {
            $this->setFileInfo(new FileInfo());
        }
        $this->getFileInfo()->setHidden($hidden);
    }
    
    private function setOwnerName($ownerName) {
        if ($this->getFileInfo() === null) {
            $this->setFileInfo(new FileInfo());
        }
        $this->getFileInfo()->setOwnerName($ownerName);
    }
    
    private function setLockedByName($lockedByName) {
        if ($this->getFileInfo() === null) {
            $this->setFileInfo(new FileInfo());
        }
        $this->getFileInfo()->setLockedByName($lockedByName);
    }
    
    
/* ------------------------   OTHER METHODS   ---------------------------- */
    
    /**
     * Determines whether the current DataFile object is FS root. When the
     * arguments are specified, the real object attributes are replaced
     * by the given parameters. So the method calculates if "the object would
     * be root with those parameters". The last parameter tells whether
     * empty string is allowed instead of null in the root url attribute.
     * 
     * @param String $url (implicit array() because null is possible input value)
     * @param String $name
     * @param boolean $emptyStringRootUrlAllowed
     * @return boolean TRUE when the object is root, FALSE otherwise
     */
    public function isRoot($url = array(), $name = null, $emptyStringRootUrlAllowed = false) {
        if ($url === array()) {
            $url = $this->getUrl();
        }
        if ($name === null) {
            $name = $this->getName();
        }
        
        if ($name === "/" && ($url === null || ($url === "" && $emptyStringRootUrlAllowed))) {
            return true;
        } else {
            return false;
        }
    }
    
    public function isDirectory() {
        return ($this->getMimetypeText() === DataFile::DIR);
    }
    
    /**
     * Creates new DataFile object and sets URL, name and mimetype to the
     * value according to the VirtualFile given as first parameter (in fact,
     * the method converts the VirtualFile into new DataFile). When the second
     * parameter is TRUE, other attributes are set as well.
     * 
     * @param VirtualFile $virtualFile The orriginal object which will be taken
     *      info from.
     * @param int $need 
     *      Every number (power of 2) specifies single kind of
     *      information. The bit is set to 1 when we want the info, 
     *      zero otherwise (usage A|B|C means we want all A, B and C fields).
     * 
     * @return DataFile new DataFile object
     */
    public static function createFromVirtual(VirtualFile $virtualFile, $need = GenericFile::NONE) {
        
        //Timer::start("DF - create from VF", TimerLevel::DETAILED);
        if ($virtualFile->isRoot()) {
            $dataFile = new DataFile("/", null);
        } else {
            $dataFile = new DataFile(
                    $virtualFile->getName(),
                    substr($virtualFile->getUrl(), strlen(VirtualFile::VIRTUAL_ROOT))
            );
        }
        
        if ($need & GenericFile::FSTORAGE) {
            $fstorageName = dibi::fetch("SELECT [name_files_storage] FROM [files_storages] " .
                    "WHERE [id_files_storage]=%i", $virtualFile->getFstorageId());
            if ($fstorageName === false) {
                throw new InvalidArgumentException("Cannot convert fstorage ID " . $virtualFile->getFstorageId() . 
                        ", the fstorage does not exist in DB.");
            }
            $dataFile->setFstorageName($fstorageName["name_files_storage"]);
        }
        
        if ($need & GenericFile::MIMETYPE) {
            $dataFile->setMimetypeTextById($virtualFile->getMimetypeId());
        }
        
        if ($need & GenericFile::LOCKEDBY) {
            $lockedByRow = dibi::fetch("SELECT [user_name] FROM [users] WHERE [id]=%i", 
                    $virtualFile->getLockedById());
            $dataFile->setLockedByName($lockedByRow["user_name"]);
        }
        
        if ($need & GenericFile::OWNER) {
            $ownerName = dibi::fetch("SELECT [user_name] FROM [users] WHERE [id]=%i", 
                    $virtualFile->getOwnerId());
            $dataFile->setOwnerName($ownerName["user_name"]);
        }
        
        if ($need & GenericFile::BASIC_INFO) {
            $dataFile->setEncrypted($virtualFile->getEncrypted());
            $dataFile->setProtected($virtualFile->getProtected());
            $dataFile->setHidden($virtualFile->getHidden());
        }
        //Timer::stop("DF - create from VF");
        return $dataFile;
    }
    
    private function setMimetypeTextById($mimetypeId) {
        $mimetypeRow = dibi::select("[name_mimetype]")->from("[mimetypes]")
                ->where(array("id_mimetype" => $mimetypeId))->fetch();
        $this->setMimetypeText($mimetypeRow["name_mimetype"]);
    }

}
